

#define __LCD_VERSION__  "1.0"

#include "spilcd.h"
#include "spilcdfont.h"


#define SPI_LCD_TYPE    1           /* SPI接口屏幕类型（1：2.4寸SPILCD  0：1.3寸SPILCD） */  

spi_device_handle_t MY_LCD_Handle;
uint8_t lcd_buf[LCD_TOTAL_BUF_SIZE];
lcd_obj_t lcd_self;


/* LCD需要初始化一组命令/参数值。它们存储在此结构中  */
typedef struct
{
    uint8_t cmd;
    uint8_t data[16];
    uint8_t databytes; /* 数据中没有数据；比特7＝设置后的延迟；0xFF=cmds结束 */
} lcd_init_cmd_t;

/**
 * @brief       发送命令到LCD，使用轮询方式阻塞等待传输完成(由于数据传输量很少，因此在轮询方式处理可提高速度。使用中断方式的开销要超过轮询方式)
 * @param       cmd 传输的8位命令数据
 * @retval      无
 */
void lcd_write_cmd(const uint8_t cmd)
{
    LCD_WR(0);
    spi2_write_cmd(MY_LCD_Handle, cmd);
}

/**
 * @brief       发送数据到LCD，使用轮询方式阻塞等待传输完成(由于数据传输量很少，因此在轮询方式处理可提高速度。使用中断方式的开销要超过轮询方式)
 * @param       data 传输的8位数据
 * @retval      无
 */
void lcd_write_data(const uint8_t *data, int len)
{
    LCD_WR(1);
    spi2_write_data(MY_LCD_Handle, data, len);
}

/**
 * @brief       发送数据到LCD，使用轮询方式阻塞等待传输完成(由于数据传输量很少，因此在轮询方式处理可提高速度。使用中断方式的开销要超过轮询方式)
 * @param       data 传输的16位数据
 * @retval      无
 */
void lcd_write_data16(uint16_t data)
{
    uint8_t dataBuf[2] = {0,0};
    dataBuf[0] = data >> 8;
    dataBuf[1] = data & 0xFF;
    LCD_WR(1);
    spi2_write_data(MY_LCD_Handle, dataBuf,2);
}

/**
 * @brief       设置窗口大小
 * @param       xstar：左上角x轴
 * @param       ystar：左上角y轴
 * @param       xend：右下角x轴
 * @param       yend：右下角y轴
 * @retval      无
 */
void lcd_set_window(uint16_t xstar, uint16_t ystar,uint16_t xend,uint16_t yend)
{	
    uint8_t databuf[4] = {0,0,0,0};
    databuf[0] = xstar >> 8;
    databuf[1] = 0xFF & xstar;
    databuf[2] = xend >> 8;
    databuf[3] = 0xFF & xend;
    lcd_write_cmd(lcd_self.setxcmd);
    lcd_write_data(databuf,4);

    databuf[0] = ystar >> 8;
    databuf[1] = 0xFF & ystar;
    databuf[2] = yend >> 8;
    databuf[3] = 0xFF & yend;
    lcd_write_cmd(lcd_self.setycmd);
    lcd_write_data(databuf,4);

    lcd_write_cmd(lcd_self.wramcmd);    /* 开始写入GRAM */
}   

/**
 * @brief       以一种颜色清空LCD屏
 * @param       color 清屏颜色
 * @retval      无
 */
void lcd_clear(uint16_t color)
{
    uint16_t i, j;
    uint8_t data[2] = {0};

    data[0] = color >> 8;
    data[1] = color;
    
    lcd_set_window(0, 0, lcd_self.width - 1, lcd_self.height - 1);

    for(j = 0; j < LCD_BUF_SIZE / 2; j++)
    {
        lcd_buf[j * 2] =  data[0];
        lcd_buf[j * 2 + 1] =  data[1];
    }

    for(i = 0; i < (LCD_TOTAL_BUF_SIZE / LCD_BUF_SIZE); i++)
    {
        lcd_write_data(lcd_buf, LCD_BUF_SIZE);
    }
}

/**
 * @brief       在指定区域内填充单个颜色
 * @param       (sx,sy),(ex,ey):填充矩形对角坐标,区域大小为:(ex - sx + 1) * (ey - sy + 1)
 * @param       color:要填充的颜色(32位颜色,方便兼容LTDC)
 * @retval      无
 */
void lcd_fill(uint16_t sx, uint16_t sy, uint16_t ex, uint16_t ey, uint16_t color)
{
    uint16_t i;
    uint16_t j;
    uint16_t width;
    uint16_t height;

    width = ex - sx + 1;
    height = ey - sy + 1;
    lcd_set_window(sx, sy, ex, ey);

    for (i = 0; i < height; i++)
    {
        for (j = 0; j < width; j++)
        {
            lcd_write_data16(color);
        }
    }
    lcd_set_window(sx, sy, ex, ey);
}


/**
 * @brief       设置光标的位置
 * @param       Xpos：左上角x轴
 * @param       Ypos：左上角y轴
 * @retval      无
 */
void lcd_set_cursor(uint16_t xpos, uint16_t ypos)
{
    lcd_set_window(xpos,ypos,xpos,ypos);	
} 

/**
 * @brief       设置LCD的自动扫描方向(对RGB屏无效)
 * @param       dir:0~7,代表8个方向(具体定义见lcd.h)
 * @retval      无
 */
void lcd_scan_dir(uint8_t dir)
{
    uint8_t regval = 0;
    uint8_t dirreg = 0;
    uint16_t temp;

    /* 横屏时，对1963不改变扫描方向, 其他IC改变扫描方向！竖屏时1963改变方向, 其他IC不改变扫描方向 */
    if (lcd_self.dir == 1)
    {
        dir = 5;
    }

    /* 根据扫描方式 设置 0X36/0X3600 寄存器 bit 5,6,7 位的值 */
    switch (dir)
    {
        case L2R_U2D:                           /* 从左到右,从上到下 */
            regval |= (0 << 7) | (0 << 6) | (0 << 5);
            break;

        case L2R_D2U:                           /* 从左到右,从下到上 */
            regval |= (1 << 7) | (0 << 6) | (0 << 5);
            break;

        case R2L_U2D:                           /* 从右到左,从上到下 */
            regval |= (0 << 7) | (1 << 6) | (0 << 5);
            break;

        case R2L_D2U:                           /* 从右到左,从下到上 */
            regval |= (1 << 7) | (1 << 6) | (0 << 5);
            break;

        case U2D_L2R:                           /* 从上到下,从左到右 */
            regval |= (0 << 7) | (0 << 6) | (1 << 5);
            break;

        case U2D_R2L:                           /* 从上到下,从右到左 */
            regval |= (0 << 7) | (1 << 6) | (1 << 5);
            break;

        case D2U_L2R:                           /* 从下到上,从左到右 */
            regval |= (1 << 7) | (0 << 6) | (1 << 5);
            break;

        case D2U_R2L:                           /* 从下到上,从右到左 */
            regval |= (1 << 7) | (1 << 6) | (1 << 5);
            break;
    }

    dirreg = 0x36;                              /* 对绝大部分驱动IC, 由0X36寄存器控制 */
    
    uint8_t date_send[1] = {regval};
    
    lcd_write_cmd(dirreg);
    lcd_write_data(date_send,1);
    
    if (regval & 0x20)
    {
        if (lcd_self.width < lcd_self.height)   /* 交换X,Y */
        {
            temp = lcd_self.width;
            lcd_self.width = lcd_self.height;
            lcd_self.height = temp;
        }
    }
    else
    {
        if (lcd_self.width > lcd_self.height)   /* 交换X,Y */
        {
            temp = lcd_self.width;
            lcd_self.width = lcd_self.height;
            lcd_self.height = temp;
        }
    }
    
    lcd_set_window(0, 0, lcd_self.width,lcd_self.height);
}

/**
 * @brief       设置LCD显示方向
 * @param       dir:0,竖屏; 1,横屏
 * @retval      无
 */
void lcd_display_dir(uint8_t dir)
{
    lcd_self.dir = dir;
    
    if (lcd_self.dir == 0)                  /* 竖屏 */
    {
        lcd_self.width      = 240;
        lcd_self.height     = 320;
        lcd_self.wramcmd    = 0X2C;
        lcd_self.setxcmd    = 0X2A;
        lcd_self.setycmd    = 0X2B;
    }
    else                                    /* 横屏 */
    {
        lcd_self.width      = 320;          /* 默认宽度 */
        lcd_self.height     = 240;          /* 默认高度 */
        lcd_self.wramcmd    = 0X2C;
        lcd_self.setxcmd    = 0X2A;
        lcd_self.setycmd    = 0X2B;
    }

    lcd_scan_dir(DFT_SCAN_DIR);             /* 默认扫描方向 */
}

/**
 * @brief       硬件复位
 * @param       self_in：LCD结构体
 * @retval      无
 */
void lcd_hard_reset(void)
{
    /* 复位显示屏 */
    LCD_RST(0);
    vTaskDelay(100);
    LCD_RST(1);
    vTaskDelay(100);
}

/**
 * @brief       绘画一个像素点
 * @param       self_in：LCD结构体
 * @param       x：x轴坐标
 * @param       y：y轴坐标
 * @param       color：颜色值
 * @retval      无
 */
void lcd_draw_pixel(uint16_t x, uint16_t y, uint16_t color)
{
    lcd_set_cursor(x, y);
    lcd_write_data16(color);
}

/**
 * @brief       画线函数(直线、斜线)
 * @param       x1,y1   起点坐标
 * @param       x2,y2   终点坐标
 * @param       color 填充颜色
 * @retval      无
 */
void lcd_draw_line(uint16_t x1, uint16_t y1, uint16_t x2, uint16_t y2, uint16_t color)
{
    uint16_t t; 
    int xerr = 0, yerr = 0, delta_x, delta_y, distance; 
    
    int incx, incy, urow, ucol; 

    delta_x = x2 - x1;                      /* 计算坐标增量 */
    delta_y = y2 - y1; 
    urow = x1; 
    ucol = y1; 
    
    if (delta_x > 0)
    {
        incx = 1;                           /* 设置单步方向 */
    }
    else if (delta_x == 0)
    {
        incx = 0;                           /* 垂直线 */
    }
    else
    {
        incx =-1;
        delta_x =-delta_x;
    } 
    if(delta_y > 0)
    {
        incy = 1; 
    }
    else if(delta_y == 0)
    {
        incy = 0;                           /* 水平线 */
    }
    else
    {
        incy =-1;
        delta_y=-delta_y;
    } 
    
    if( delta_x>delta_y)
    {
        distance = delta_x;                 /* 选取基本增量坐标轴 */
    }
    else
    {
        distance = delta_y; 
    }
    
    for (t = 0;t <= distance + 1;t++ )      /* 画线输出 */
    {
        lcd_draw_pixel(urow,ucol,color);    /* 画点 */ 
        xerr += delta_x ; 
        yerr += delta_y ; 
        
        if(xerr>distance)
        { 
            xerr -= distance; 
            urow += incx; 
        } 
        
        if (yerr > distance)
        { 
            yerr -= distance; 
            ucol += incy; 
        } 
    } 
}

/**
 * @brief       画水平线
 * @param       x0,y0: 起点坐标
 * @param       len  : 线长度
 * @param       color: 矩形的颜色
 * @retval      无
 */
void lcd_draw_hline(uint16_t x, uint16_t y, uint16_t len, uint16_t color)
{
    if ((len == 0) || (x > lcd_self.width) || (y > lcd_self.height))return;

    lcd_fill(x, y, x + len - 1, y, color);
}

/**
 * @brief       画一个矩形
 * @param       x1,y1   起点坐标
 * @param       x2,y2   终点坐标
 * @param       color 填充颜色
 * @retval      无
 */
void lcd_draw_rectangle(uint16_t x0, uint16_t y0, uint16_t x1, uint16_t y1,uint16_t color)
{
    lcd_draw_line(x0, y0, x1, y0,color);
    lcd_draw_line(x0, y0, x0, y1,color);
    lcd_draw_line(x0, y1, x1, y1,color);
    lcd_draw_line(x1, y0, x1, y1,color);
}





/**
 * @brief       m^n函数
 * @param       m,n     输入参数
 * @retval      m^n次方
 */
uint32_t lcd_pow(uint8_t m, uint8_t n)
{
    uint32_t result = 1;

    while(n--)result *= m;

    return result;
}




/**
 * @brief       打开LCD
 * @param       self_in：SPI控制块
 * @retval      mp_const_none：初始化成功
 */
void lcd_on(void)
{
    LCD_PWR(1);
    vTaskDelay(10);
}

/**
 * @brief       关闭LCD
 * @param       self_in：SPI控制块
 * @retval      mp_const_none：初始化成功
 */
void lcd_off(void)
{
    LCD_PWR(0);
    vTaskDelay(10);
}

/**
 * @brief       LCD初始化
 * @param       无
 * @retval      无
 */
void lcd_init(void)
{
    int cmd = 0;
    esp_err_t ret = 0;
    
    lcd_self.dir = 0;
    lcd_self.wr = LCD_NUM_WR;                                       /* 配置WR引脚 */
    lcd_self.cs = LCD_NUM_CS;                                       /* 配置CS引脚 */
    
    gpio_config_t gpio_init_struct;

    /* SPI驱动接口配置 */
    spi_device_interface_config_t devcfg = {
        .clock_speed_hz = 60 * 1000 * 1000,                         /* SPI时钟 */
        .mode = 0,                                                  /* SPI模式0 */
        .spics_io_num = lcd_self.cs,                                /* SPI设备引脚 */
        .queue_size = 7,                                            /* 事务队列尺寸 7个 */
    };
    
    /* 添加SPI总线设备 */
    ret = spi_bus_add_device(SPI2_HOST, &devcfg, &MY_LCD_Handle);   /* 配置SPI总线设备 */
    ESP_ERROR_CHECK(ret);

    gpio_init_struct.intr_type = GPIO_INTR_DISABLE;                 /* 失能引脚中断 */
    gpio_init_struct.mode = GPIO_MODE_OUTPUT;                       /* 配置输出模式 */
    gpio_init_struct.pin_bit_mask = 1ull << lcd_self.wr;            /* 配置引脚位掩码 */
    gpio_init_struct.pull_down_en = GPIO_PULLDOWN_DISABLE;          /* 失能下拉 */
    gpio_init_struct.pull_up_en = GPIO_PULLUP_ENABLE;               /* 使能下拉 */
    gpio_config(&gpio_init_struct);                                 /* 引脚配置 */

    lcd_hard_reset();                                               /* LCD硬件复位 */

    /* 初始化代码 */
#if SPI_LCD_TYPE                                                    /* 对2.4寸LCD寄存器进行设置 */
    lcd_init_cmd_t ili_init_cmds[] =
    {
        {0x11, {0}, 0x80},//退出睡眠模式
        {0x36, {0x00}, 1},//扫描方式
        {0x3A, {0x65}, 1},//设置像素格式
        {0X21, {0}, 0x80},
        {0x29, {0}, 0x80},
        {0, {0}, 0xff},
    };

#else                                                               /* 不为0则视为使用1.3寸SPILCD屏，那么屏幕将不会反显 */
    lcd_init_cmd_t ili_init_cmds[] =
    {
        {0x11, {0}, 0x80},
        {0x36, {0x00}, 1},
        {0x3A, {0x65}, 1},
        {0xB2, {0x0C, 0x0C, 0x00, 0x33,0x33}, 5},
        {0xB7, {0x75}, 1},
        {0xBB, {0x1C}, 1},
        {0xC0, {0x2c}, 1},
        {0xC2, {0x01}, 1},
        {0xC3, {0x0F}, 1},
        {0xC4, {0x20}, 1},
        {0xC6, {0X01}, 1},
        {0xD0, {0xA4,0xA1}, 2},
        {0xE0, {0xD0, 0x04, 0x0D, 0x11, 0x13, 0x2B, 0x3F, 0x54, 0x4C, 0x18, 0x0D, 0x0B, 0x1F, 0x23}, 14},
        {0xE1, {0xD0, 0x04, 0x0C, 0x11, 0x13, 0x2C, 0x3F, 0x44, 0x51, 0x2F, 0x1F, 0x1F, 0x20, 0x23}, 14},
        {0X21, {0}, 0x80},
        {0x29, {0}, 0x80},
        {0, {0}, 0xff},
    };
#endif

    /* 循环发送设置所有寄存器 */
    while (ili_init_cmds[cmd].databytes != 0xff)
    {
        lcd_write_cmd(ili_init_cmds[cmd].cmd);
        lcd_write_data(ili_init_cmds[cmd].data, ili_init_cmds[cmd].databytes & 0x1F);
        
        if (ili_init_cmds[cmd].databytes & 0x80)
        {
            vTaskDelay(120);
        }
        
        cmd++;
    }

    lcd_display_dir(1);                                             /* 设置屏幕方向 */
    LCD_PWR(1);
    lcd_clear(WHITE);                                               /* 清屏 */
}



/**** ************************************************************************************************
	* 函    数：显示单个字符
	* 参    数：x,y显示坐标
				num 要显示的字符
				fc 字的颜色
				bc 字的背景色
				sizey 字号
				mode	0非叠加模式	1叠加模式
	* 返 回 值：无
********************************************************************************************************	*/
void lcd_show_char(uint16_t x,uint16_t y,uint8_t num,uint16_t fc,uint16_t bc,uint8_t sizey,uint8_t mode)
{
	uint8_t temp,sizex,t,sta=8,m=0;
	uint16_t i,TypefaceNum;	// 一个字符所占字节大小
	uint16_t x0=x;
	sizex=sizey/2;
	TypefaceNum=(sizex/8+((sizex%8)?1:0))*sizey;
	num=num-' ';		// 得到偏移后的值
	lcd_set_window(x,y,x+sizex-1,y+sizey-1);			// 设置光标位置 
	for(i=0;i<TypefaceNum;i++)
	{ 
		if(sizey==12)
            temp=asc2_1206[num][i];			// 调用6x12字体
		else if(sizey==16)
            temp=asc2_1608[num][i];		// 调用8x16字体
		else if(sizey==24)
            temp=asc2_2412[num][i];		// 调用12x24字体
		else if(sizey==32)
            temp=asc2_3216[num][i];		// 调用16x32字体
		else 
            return;

        if(sizey==24)
        {sta=(i%2==0)?8:4;}
        else
        {sta=8;}
		for(t=0;t<sta;t++)
		{
			if(!mode)	// 非叠加模式
			{
				if(temp&(0x80>>t)) 
                    lcd_write_data16(fc);
				else 
                    lcd_write_data16(bc);
				m++;
				if(m>=sizex)
				{
					m=0;
					break;
				}
			}
			else	// 叠加模式
			{
				if(temp&(0x80>>t)) 
                    lcd_draw_pixel(x,y,fc);	// 画一个点
				x++;
				if((x-x0)==sizex)
				{
					x=x0;
					y++;
					break;
				}
			}
		}
	}   	 	  
}

/**** *****************************************************************************************
	* 函    数：显示字符串
	* 参    数：x,y显示坐标
				*p 要显示的字符串
				fc 字的颜色
				bc 字的背景色
				sizey 字号
				mode	0非叠加模式	1叠加模式
	* 返 回 值：无
*************************************************************************************************	*/
void lcd_show_string(uint16_t x,uint16_t y,char *p,uint16_t fc,uint16_t bc,uint8_t sizey,uint8_t mode)
{         
	while(*p!='\0')
	{       
		lcd_show_char(x,y,(uint8_t)*p,fc,bc,sizey,mode);
		x+=sizey/2;
		p++;
	}  
}


/*****************************************************************************************************
	* 函    数：显示两位小数变量
	* 参    数：x,y显示坐标
				num 要显示float数据
				len 要显示的位数，不包含小数点的数据为数
				fc 字的颜色
				bc 字的背景色
				sizey 字号
	* 返 回 值：无
*******************************************************************************************************/
void lcd_show_floatNum(uint16_t x,uint16_t y,float num,uint8_t len,uint16_t fc,uint16_t bc,uint8_t sizey)
{
	uint8_t t,temp,sizex;
	uint16_t num1;
	sizex=sizey/2;
	num1=num*100;
	for(t=0;t<len;t++)
	{
		temp=(num1/lcd_pow(10,len-t-1))%10;
		if(t==(len-2))
		{
			lcd_show_char(x+(len-2)*sizex,y,'.',fc,bc,sizey,0);
			t++;
			len+=1;
		}
            lcd_show_char(x+t*sizex,y,temp+48,fc,bc,sizey,0);
	}
}
/************************************************************************************************
	* 函    数：显示整数变量
	* 参    数：x,y显示坐标
				num 要显示数据
				len 要显示的位数
				fc 字的颜色
				bc 字的背景色
				sizey 字号
	* 返 回 值：无
**************************************************************************************************/
void lcd_show_num(uint16_t x,uint16_t y,uint16_t num,uint8_t len,uint16_t fc,uint16_t bc,uint8_t sizey)
{         	
	uint8_t  t,temp;
	uint8_t  enshow=0;
	uint8_t  sizex=sizey/2;
	for(t=0;t<len;t++)
	{
		temp=(num/lcd_pow(10,len-t-1))%10;
		if(enshow==0&&t<(len-1))
		{
			if(temp==0)
			{
                lcd_show_char(x+t*sizex,y,' ',fc,bc,sizey,0);
				continue;
			}
            else 
            {enshow=1; }
		
		}
            lcd_show_char(x+t*sizex,y,temp+48,fc,bc,sizey,0);
	}
} 



/************************************************************************************************ 
	* 函    数：画圆(空心圆)
	* 参    数：x0, y0	圆心坐标
				r 		半径
				color	圆的颜色
	* 返 回 值：无
***************************************************************************************************/
void lcd_draw_circle(uint16_t x0,uint16_t y0,uint8_t r,uint16_t color)
{
	int a,b;
	a = 0; b = r;	  
	while(a <= b)
	{
		lcd_draw_pixel(x0-b,y0-a,color);             //	3           
		lcd_draw_pixel(x0+b,y0-a,color);             //	0           
		lcd_draw_pixel(x0-a,y0+b,color);             //	1                
		lcd_draw_pixel(x0-a,y0-b,color);             //	2             
		lcd_draw_pixel(x0+b,y0+a,color);             //	4               
		lcd_draw_pixel(x0+a,y0-b,color);             //	5
		lcd_draw_pixel(x0+a,y0+b,color);             //	6 
		lcd_draw_pixel(x0-b,y0+a,color);             //	7
		a++;
		if((a*a+b*b)>(r*r))		// 判断要画的点是否过远
		{
			b--;
		}
	}
}
/************************************************************************************************ 
	* 函    数：画圆(实心圆)
	* 参    数：x0, y0	圆心坐标
				r 		半径
				color	圆的颜色
	* 返 回 值：无
***************************************************************************************************/
void lcd_draw_filled_circle(uint16_t x0, uint16_t y0, uint8_t r, uint16_t color) 
{
    int a = 0;
    int b = r;
    int d = 1 - r;
    
    // 处理圆心行（y=0）
    lcd_draw_hline(x0 - r, y0, 2 * r + 1, color);
    
    while (a <= b) {
        // 绘制上下对称的水平线
        if (a > 0) {  // 避免重复绘制圆心行
            lcd_draw_hline(x0 - a, y0 + b, 2 * a + 1, color);
            lcd_draw_hline(x0 - a, y0 - b, 2 * a + 1, color);
            lcd_draw_hline(x0 - b, y0 + a, 2 * b + 1, color);
            lcd_draw_hline(x0 - b, y0 - a, 2 * b + 1, color);
        }
        
        // 更新决策参数
        if (d < 0) {
            d += 2 * a + 3;
        } else {
            d += 2 * (a - b) + 5;
            b--;
        }
        a++;
    }
}


/**
  * 功能: 支持UTF8/GB2312编码的中文字符串显示
  * 参数: 
  *   x,y - 起始坐标
  *   str - 要显示的字符串(需与编码设置匹配)
  *   fc  - 前景色(RGB565格式)
  *   bc  - 背景色(RGB565格式)
  *   sizey - 字体高度(须为16的倍数)
  *   mode - 0:覆盖模式 / 1:叠加模式
  * 说明: 
  *   1.需在工程中定义OLED_CHARSET_GB2312或OLED_CHARSET_UTF8
  *   2.字库数组SPILCD_CF16必须以空元素结尾
  */
void lcd_showChinese16x16(uint16_t x, uint16_t y, char *str, uint16_t fc, uint16_t bc, uint8_t sizey, uint8_t mode)
{
    uint16_t x0 = x;                            // 初始X坐标备份
    char SingleChar[5] = {0};                   // 存储单个字符(含结束符)
    uint8_t charBytes = 0;                      // 当前字符占字节数
    
    

    while (*str != '\0') {
        /* ----------------- 编码解析阶段 ----------------- */
#ifdef OLED_CHARSET_UTF8
        // UTF8编码解析
        if ((*str & 0x80) == 0x00) {           // ASCII字符(1字节)
            lcd_show_char(x, y, *str, fc,bc,sizey, 0);
            x += sizey/2;
            str++;
            continue;
        } 
        else if ((*str & 0xF0) == 0xE0) {      // 中文UTF8(3字节)
            SingleChar[0] = *str++;
            if(*str == '\0') break;
            SingleChar[1] = *str++;
            if(*str == '\0') break;
            SingleChar[2] = *str++;
            charBytes = 3;
        }
#elif defined(OLED_CHARSET_GB2312)
        // GB2312编码解析
        if ((*str & 0x80) == 0x00) {           // ASCII字符(1字节)
            lcd_show_char(x, y, *str, fc,bc,sizey, 0);
            x += sizey/2;
            str++;
            continue;
        } else {                                // 中文GB2312(2字节)
            SingleChar[0] = *str++;
            if(*str == '\0') break;
            SingleChar[1] = *str++;
            charBytes = 2;
        }
#endif
        /* ----------------- 字库查找阶段 ----------------- */
        uint16_t pIndex = 0;
        uint8_t found = 0;
        // 遍历字库(需确保字库以空元素结尾!)
        for (; strcmp(SPILCD_CF16[pIndex].Index, "") != 0; pIndex++) {
            if (strncmp(SPILCD_CF16[pIndex].Index, SingleChar, charBytes) == 0) {
                found = 1;
                break;
            }
        }
        if (!found) {                           // 未找到字符
            x += 16;                            // 留出空白位置
            charBytes = 0;
            continue;
        }

        /* ----------------- 数据显示阶段 ----------------- */
        // 关键修复1: 必须设置显示窗口
        if(!mode) {
            lcd_set_window(x, y, x+15, y+15);   // 16x16区域
        }
        
        // 关键修复2: 修正点阵数据解析顺序
        for (uint8_t i=0; i<32; i++) {         // 32字节/字符
            uint8_t byteData = SPILCD_CF16[pIndex].Data[i];
            
            // 关键修复3: 处理双字节行(每行16像素=2字节)
            if(i%2 == 0) {                      // 每行首字节处理8位
                for(uint8_t bit=0; bit<8; bit++){
                    if(!mode) {
                        lcd_write_data16( (byteData & (0x80>>bit)) ? fc : bc );
                    } else {
                        uint16_t px = x + (i/2)*8 + bit;
                        uint16_t py = y + (i%2)*8;
                        lcd_draw_pixel(px, py, (byteData & (0x80>>bit)) ? fc : bc);
                    }
                }
            } else {                            // 每行次字节处理剩余8位
                for(uint8_t bit=0; bit<8; bit++){
                    if(!mode) {
                        lcd_write_data16( (byteData & (0x80>>bit)) ? fc : bc );
                    } else {
                        uint16_t px = x + (i/2)*8 + bit;
                        uint16_t py = y + (i%2)*8;
                        lcd_draw_pixel(px, py, (byteData & (0x80>>bit)) ? fc : bc);
                    }
                }
            }
        }

        /* ----------------- 坐标更新阶段 ----------------- */
        if(!mode) {
            x += 16;                            // 覆盖模式直接偏移
        } else {
            x = x0 + (x - x0 + 16);             // 叠加模式按实际绘制位置偏移
        }
        charBytes = 0;                          // 重置字符长度
    } // end while
}

/**
  * 功能: 支持UTF8/GB2312编码的中文字符串显示
  * 参数: 
  *   x,y - 起始坐标
  *   str - 要显示的字符串(需与编码设置匹配)
  *   fc  - 前景色(RGB565格式)
  *   bc  - 背景色(RGB565格式)
  *   sizey - 字体高度(须为24的倍数)
  *   mode - 0:覆盖模式 / 1:叠加模式
  * 说明: 
  *   1.需在工程中定义OLED_CHARSET_GB2312或OLED_CHARSET_UTF8
  *   2.字库数组SPILCD_CF24必须以空元素结尾
  */
void lcd_showChinese24x24(uint16_t x, uint16_t y, char *str, uint16_t fc, uint16_t bc, uint8_t sizey, uint8_t mode)
{
    uint16_t x0 = x;                            // 初始X坐标备份
    char SingleChar[5] = {0};                   // 存储单个字符(含结束符)
    uint8_t charBytes = 0;                      // 当前字符占字节数
    
    

    while (*str != '\0') {
        /* ----------------- 编码解析阶段 ----------------- */
#ifdef OLED_CHARSET_UTF8
        // UTF8编码解析
        if ((*str & 0x80) == 0x00) {           // ASCII字符(1字节)
            lcd_show_char(x, y, *str, fc,bc,sizey, 0);
            x += sizey/2;
            str++;
            continue;
        } 
        else if ((*str & 0xF0) == 0xE0) {      // 中文UTF8(3字节)
            SingleChar[0] = *str++;
            if(*str == '\0') break;
            SingleChar[1] = *str++;
            if(*str == '\0') break;
            SingleChar[2] = *str++;
            charBytes = 3;
        }
#elif defined(OLED_CHARSET_GB2312)
        // GB2312编码解析
        if ((*str & 0x80) == 0x00) {           // ASCII字符(1字节)
            lcd_show_char(x, y, *str, fc,bc,sizey, 0);
            x += sizey/2;
            str++;
            continue;
        } else {                                // 中文GB2312(2字节)
            SingleChar[0] = *str++;
            if(*str == '\0') break;
            SingleChar[1] = *str++;
            charBytes = 2;
        }
#endif

        /* --------------- 字库查找阶段 --------------- */
        uint16_t pIndex = 0;
        uint8_t found = 0;
        for (; strcmp(SPILCD_CF24[pIndex].Index, "") != 0; pIndex++) {
            if (strncmp(SPILCD_CF24[pIndex].Index, SingleChar, charBytes) == 0) {
                found = 1;
                break;
            }
        }
        if (!found) {
            x += 24;    // 未找到字符留空
            charBytes = 0;
            continue;
        }

        /* --------------- 数据显示阶段 --------------- */
        if (!mode) {
            lcd_set_window(x, y, x+23, y+23); // 设置24x24窗口
        }
        
        // 处理72字节点阵数据
        for (uint16_t i=0; i<72; i++) { 
            uint8_t byteData = SPILCD_CF24[pIndex].Data[i];
            
            // 每行3字节处理24像素
            for (uint8_t bit=0; bit<8; bit++) {
                uint16_t px = x + (i%3)*8 + bit; // 计算X坐标
                uint16_t py = y + (i/3);         // 计算Y坐标
                
                if (!mode) { // 覆盖模式
                    lcd_write_data16((byteData & (0x80>>bit)) ? fc : bc);
                } else {     // 叠加模式
                    if(px <= x+23 && py <= y+23) { // 边界检查
                        lcd_draw_pixel(px, py, 
                            (byteData & (0x80>>bit)) ? fc : bc);
                    }
                }
            }
        }

        /* --------------- 坐标更新 --------------- */
        if (!mode) x += 24;   // 覆盖模式直接偏移
        else x = x0 + (x - x0 + 24); // 叠加模式计算新位置
        charBytes = 0;
    }
}










#if 0
/**********************************
 * @brief       程序入口
 * @param       无
 * @retval      无
 ***********************************/
i2c_obj_t i2c0_master;
void app_main(void)
{
    esp_err_t ret;
    ret = nvs_flash_init();                                         /* 初始化NVS */
    if (ret == ESP_ERR_NVS_NO_FREE_PAGES || ret == ESP_ERR_NVS_NEW_VERSION_FOUND)
    {
        ESP_ERROR_CHECK(nvs_flash_erase());
        ret = nvs_flash_init();
    }
    
    LED_init();
    usart_init(115200); 
    i2c0_master = iic_init(I2C_NUM_0);  /* 初始化IIC0 */
    spi2_init();                        /* 初始化SPI2 */
    xl9555_init(i2c0_master);           /* IO扩展芯片初始化 spi上扩展端口要用*/
    lcd_init();                         /* 初始化LCD */

    lcd_clear(WHITE);
    lcd_show_floatNum(0,0,3.14,3,YELLOW,BLUE,24);
    lcd_show_string(0, 100, "ABCD", GREEN, BLACK, 24, 0);
    lcd_draw_circle(200,200,10,CYAN);
    //lcd_draw_filled_circle(120, 120, 10,MAGENTA); 
    lcd_showChinese16x16(160, 80, "你好", RED, BLACK, 16, 0);
    lcd_showChinese24x24(260, 80, "你好", RED, BLACK, 24, 0);
    while(1)
    {
        
        printf("hello friday!\r\n");
        LED_TOGGLE();
        
        vTaskDelay(500);
    }
}


#endif 